#pragma once

#include <stdlib.h>

#include <vector>

namespace hawking {
namespace indexlib {

class FixedSizeAllocator {
public:
    FixedSizeAllocator(size_t fixed_size) {
        fixed_size_ = fixed_size > sizeof(void*) ? fixed_size : sizeof(void*);
        buf_vec_.reserve(1024);
    }
    ~FixedSizeAllocator() {
        Clear();
    }

    static const size_t COUNT_PER_BLOCK = 4 * 1024;

    void* Allocate() {
        void* ret = nullptr;
        ++count_;
        if (free_list_) {
            ret = free_list_;
            free_list_ = *(static_cast<void**>(free_list_));
        } else if (free_ < end_) {
            ret = free_;
            free_ += fixed_size_;
        } else {
            start_ = (char*)::malloc(COUNT_PER_BLOCK * fixed_size_);
            if (!start_) {
                --count_;
                return nullptr;
            }

            end_ = start_ + COUNT_PER_BLOCK * fixed_size_;
            free_ = start_ + fixed_size_;
            buf_vec_.push_back(start_);
            ret = start_;
        }

        return ret;
    }

    void Free(void* ptr) {
        --count_;
        *reinterpret_cast<void**>(ptr) = free_list_;
        free_list_ = ptr;
    }

    size_t Count() const {
        return count_;
    }

    void Clear() {
        for (auto it : buf_vec_) {
            ::free(it);
        }

        buf_vec_.clear();
        start_ = nullptr;
        free_ = nullptr;
        end_ = nullptr;
        free_list_ = nullptr;
        count_ = 0;
    }

private:
    size_t fixed_size_;
    char* start_ = nullptr;
    char* free_ = nullptr;
    char* end_ = nullptr;
    void* free_list_ = nullptr;
    size_t count_ = 0;
    std::vector<char*> buf_vec_;
};

}
}
